#!/usr/bin/env python3
# Copyright (c) 2022-2024 maminjie <canpool@163.com>
# SPDX-License-Identifier: MulanPSL-2.0

import os
import argparse
import logging
import datetime

logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO)

CUR_DIR = os.getcwd()

OBS_REPOS = {
    "standard_x86_64": (
        "x86_64",
        ),
    "standard_aarch64": (
        "aarch64",
        )
}

DIST_REPOS = (
    "aarch64",
    "x86_64",
    "source",
)


def create_update_tree(dir):
    try:
        for repo in DIST_REPOS:
            os.makedirs(os.path.join(dir, repo, "Packages"), exist_ok=True)
    except OSError:
        return False
    return True


def get_pkg_list(pkg_file):
    pkgs = []
    if not os.path.isfile(pkg_file):
        return pkgs
    with open(pkg_file, 'r', encoding="utf-8") as f:
        pkgs = [x.strip() for x in f.readlines() if x.strip()]
    return pkgs


def get_binaries(prj, pkg, repo, arch, dir):
    cmd = "osc getbinaries {} {} {} {} --sources --debug -q -d {}/binaries >/dev/null".format(prj, pkg, repo, arch, dir)
    if os.system(cmd):
        logging.error("get binaries of {}/{}/{} failed".format(repo, arch, pkg))
        return False
    os.system("cp {0}/binaries/*.src.rpm {0}/source/Packages".format(dir))
    os.system("rm -f {}/binaries/*.src.rpm".format(dir))
    os.system("cp {0}/binaries/*.rpm {0}/{1}/Packages".format(dir, arch))
    os.system("rm -rf {}/binaries".format(dir))
    return True


def do_create(args):
    logging.info("create update start")
    if args.dir:
        update_dir = os.path.realpath(args.dir)
        if not os.path.isdir(update_dir):
            logging.error("{} is not directory".format(args.dir))
            return False
    else:
        dt = datetime.datetime.now().strftime("%Y%m%d")
        update_dir = "update_{}".format(dt)
        update_dir = os.path.join(CUR_DIR, update_dir)
    if not create_update_tree(update_dir):
        logging.error("create update tree failed")
        return False
    pkgs = []
    if args.pkg:
        pkgs = args.pkg
    elif args.file:
        pkgs = get_pkg_list(os.path.realpath(args.file))
    if not pkgs:
        logging.error("no packages")
        return False
    i = 0
    for pkg in pkgs:
        i += 1
        logging.info("{}) {}".format(i, pkg))
        for repo, archs in OBS_REPOS.items():
            for arch in archs:
                get_binaries(args.project, pkg, repo, arch, update_dir)
    logging.info("create update end")
    return True


def create_list_tree(dir):
    try:
        os.makedirs(dir, exist_ok=True)
    except OSError:
        return False
    return True


def gen_rpm_list(prj, pkg, repo, arch, dir):
    rpmlist_arch = os.path.join(dir, "rpmlist.{}.txt".format(arch))
    cmd = "osc ls -b {} {} {} {}".format(prj, pkg, repo, arch)
    cmd += " | grep \.rpm$ | grep -v \.src.rpm$"
    cmd += " >> {}".format(rpmlist_arch)
    if os.system(cmd):
        logging.error("gen list of {}/{}/{} failed".format(repo, arch, pkg))
        return False
    os.system("echo >> {}".format(rpmlist_arch))
    return True


def do_list(args):
    logging.info("list update start")
    if args.dir:
        update_dir = os.path.realpath(args.dir)
        if not os.path.isdir(update_dir):
            logging.error("{} is not directory".format(args.dir))
            return False
    else:
        dt = datetime.datetime.now().strftime("%Y%m%d")
        update_dir = "list_{}".format(dt)
        update_dir = os.path.join(CUR_DIR, update_dir)
    if not create_list_tree(update_dir):
        logging.error("create list tree failed")
        return False
    pkgs = []
    if args.pkg:
        pkgs = args.pkg
    elif args.file:
        pkgs = get_pkg_list(os.path.realpath(args.file))
    if not pkgs:
        logging.error("no packages")
        return False
    i = 0
    for pkg in pkgs:
        i += 1
        logging.info("{}) {}".format(i, pkg))
        for repo, archs in OBS_REPOS.items():
            for arch in archs:
                gen_rpm_list(args.project, pkg, repo, arch, update_dir)
    logging.info("list update end")
    return True


def create_repo(dir):
    if os.path.isdir(dir):
        os.system("createrepo {}".format(dir))


def do_publish(args):
    update_dir = os.path.realpath(args.dir)
    if not os.path.isdir(update_dir):
        logging.error("{}: no such directory".format(args.dir))
        return False
    for repo in DIST_REPOS:
        create_repo(os.path.join(update_dir, repo))
    return True


def do_main(args):
    print("try -h/--help for more details.")


def main():
    parser = argparse.ArgumentParser(description="release rpm update packages tool")
    parser.set_defaults(func=do_main)

    subparsers = parser.add_subparsers(help="updater sub-commands")
    # create
    subparser = subparsers.add_parser("create", aliases=["c"], formatter_class=argparse.RawTextHelpFormatter,
                                      help="create update directory and add rpm packages")
    subparser.add_argument("project", type=str, metavar="PROJECT", help="obs project name")
    group = subparser.add_mutually_exclusive_group()
    group.add_argument("-p", "--pkg", type=str, nargs='+', metavar="PACKAGE", help="package name, support multiple")
    group.add_argument("-f", "--file", type=str, metavar="FILE", help="packages file (one per line)")
    subparser.add_argument("--dir", type=str, metavar="DIR", help="update directory, default is update_yyyymmdd")
    subparser.set_defaults(func=do_create)
    # list
    subparser = subparsers.add_parser("list", aliases=["ls"], formatter_class=argparse.RawTextHelpFormatter,
                                      help="list updated rpm packages")
    subparser.add_argument("project", type=str, metavar="PROJECT", help="obs project name")
    group = subparser.add_mutually_exclusive_group()
    group.add_argument("-p", "--pkg", type=str, nargs='+', metavar="PACKAGE", help="package name, support multiple")
    group.add_argument("-f", "--file", type=str, metavar="FILE", help="packages file (one per line)")
    subparser.add_argument("--dir", type=str, metavar="DIR", help="update directory, default is list_yyyymmdd")
    subparser.set_defaults(func=do_list)
    # publish
    subparser = subparsers.add_parser("publish", aliases=["p"], formatter_class=argparse.RawTextHelpFormatter,
                                      help="publish the update repo")
    subparser.add_argument("dir", type=str, metavar="DIR", help="update directory")
    subparser.set_defaults(func=do_publish)

    args = parser.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()
